package com.wx.task.checkbill;

import cn.hutool.core.bean.BeanUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.*;

/**
 * 对账 version : 4.0
 * 需求：
 * 1. 查询订单库 -- 寻找未对账的订单  getOrders
 * 2. 查询派送库 -- 寻找派送信息     getDOrders
 * 3. 检查两方信息                 check
 * 4. 有差异的数据，存入差异库中      saveDiff
 *
 * @author wang.xun
 * @date 2023/07/31
 */
@Slf4j
public class CheckBillV4 {

    final static List<Order> orders = Arrays.asList(new Order(1, 1), new Order(2, 1), new Order(3, 2));
    final static List<Order> dOrders = Arrays.asList(new Order(1, 1), new Order(2, 1), new Order(3, 1));
    final static List<Order> diffOrders = new ArrayList<>();


    /**
     * 订单队列
     */
    final static Vector<Order> orderQueue = new Vector<>();

    /**
     * 物流订单队列
     */
    final static  Vector<Order> dOrderQueue = new Vector<>();


    public Order getOrders(int index) {

        return orders.get(index);
    }


    public Order getDOrders(int index) {

        return dOrders.get(index);
    }

    /**
     * 检查
     * 从列队头中获取相应元素，进行出队操作
     */
    public void check() {

        Order order = orderQueue.remove(0);
        Order Dorder = dOrderQueue.remove(0);
        if (!order.equals(Dorder)) {
            save(order);
        }
    }

    public void save(Order order) {
        diffOrders.add(order);
    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    static class Order {
        private int orderId;
        private int status;

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            Order order = (Order) o;
            return orderId == order.orderId && status == order.status;
        }

        @Override
        public int hashCode() {
            return Objects.hash(orderId, status);
        }
    }


    /**
     * v4.0 版本
     * countDownLatch 门闩每次都要新建
     * check 也可以与数据生成进行解耦，将数据放置到队列中，生产者进行生产
     * check 消费者进行获取 利用两个队列 一个为订单队列 一个为物流队列
     * CyclicBarrier 可以设置回调函数，当满足条件时就可以执行回调函数
     *
     * CountDownLatch 主要用来解决一个线程等待多个线程的场景，可以类比旅游团团长要等待所有的游客到齐才能去下一个景点；
     * 而 CyclicBarrier 是一组线程之间互相等待，更像是几个驴友之间不离不弃。除此之外 CountDownLatch 的计数器是不能循环利用的，
     * 也就是说一旦计数器减到 0，再有线程调用 await()，该线程会直接通过。但CyclicBarrier 的计数器是可以循环利用的，
     * 而且具备自动重置的功能，一旦计数器减到 0 会自动重置到你设置的初始值。除此之外，CyclicBarrier 还可以设置回调函数，可以说是功能丰富。
     * @param args arg游戏
     */
    public static void main(String[] args) {
        CheckBillV4 bill = new CheckBillV4();

        // 订单获取线程池
        ExecutorService infoPool = Executors.newFixedThreadPool(2);
        // 检查线程池
        ExecutorService checkPool = Executors.newFixedThreadPool(1);

        // 栅栏 拔萝卜，得够那么多的人才能拔出来，进行回调函数
        CyclicBarrier brier = new CyclicBarrier(2,()->{
            bill.check();
        });


        for (int i = 0; i < 3; i++) {

            final int index = i;

            infoPool.execute(() -> {
                Order temp = bill.getOrders(index);
                orderQueue.add(temp);
                try {
                    brier.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } catch (BrokenBarrierException e) {
                    throw new RuntimeException(e);
                }
            });

            infoPool.execute(() -> {
                Order temp = bill.getDOrders(index);
                dOrderQueue.add(temp);
                try {
                    brier.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } catch (BrokenBarrierException e) {
                    throw new RuntimeException(e);
                }
            });

        }
        diffOrders.forEach(order -> log.info("对账后，差异化的数据 :{}", order));
        // 关闭线程池
        infoPool.shutdown();
        checkPool.shutdown();
    }
}
